home *** CD-ROM | disk | FTP | other *** search
/ Amiga Plus Leser 15 / Amiga Plus Leser CD 15.iso / Tools / Development / MosaicSRC / libwww2 / HTWAIS.c < prev    next >
Encoding:
C/C++ Source or Header  |  2002-03-13  |  33.1 KB  |  1,169 lines

  1. /*    WorldWideWeb - Wide Area Informaion Server Access    HTWAIS.c
  2. **    ==================================================
  3. **
  4. **    This module allows a WWW server or client to read data from a
  5. **    remote  WAIS
  6. **  server, and provide that data to a WWW client in hypertext form.
  7. **  Source files, once retrieved, are stored and used to provide
  8. **  information about the index when that is acessed.
  9. **
  10. ** Authors
  11. **    BK    Brewster Kahle, Thinking Machines, <Brewster@think.com>
  12. **    TBL    Tim Berners-Lee, CERN <timbl@info.cern.ch>
  13. **
  14. ** History
  15. **       Sep 91    TBL adapted shell-ui.c (BK) with HTRetrieve.c from WWW.
  16. **       Feb 91    TBL Generated HTML cleaned up a bit (quotes, escaping)
  17. **                Refers to lists of sources. 
  18. **       Mar 93    TBL   Lib 2.0 compatible module made.    
  19. **
  20. ** Bugs
  21. **    Uses C stream i/o to read and write sockets, which won't work
  22. **    on VMS TCP systems.
  23. **
  24. **    Should cache connections.
  25. **
  26. **    ANSI C only as written
  27. **
  28. ** WAIS comments:
  29. **
  30. **    1.    Separate directories for different system's .o would help
  31. **    2.    Document ids are rather long!
  32. **
  33. ** WWW Address mapping convention:
  34. **
  35. **    /servername/database/type/length/document-id
  36. **
  37. **    /servername/database?word+word+word
  38. */
  39. /* WIDE AREA INFORMATION SERVER SOFTWARE:
  40.    No guarantees or restrictions.  See the readme file for the full standard
  41.    disclaimer.
  42.  
  43.    Brewster@think.com
  44. */
  45.  
  46. #ifdef DIRECT_WAIS
  47.  
  48. #define BIG 10000
  49.  
  50. /*            From WAIS
  51. **            ---------
  52. */
  53.  
  54. #include <ui.h>
  55.  
  56. #define MAX_MESSAGE_LEN 100000
  57. #define CHARS_PER_PAGE 4096 /* number of chars retrieved in each request */
  58.  
  59.  
  60. /*            FROM WWW
  61. **            --------
  62. */
  63. #define HEX_ESCAPE '%'
  64.  
  65. #include "HTUtils.h"
  66. #include "tcp.h"
  67. #include "HTParse.h"
  68. #include "HTAccess.h"        /* We implement a protocol */
  69. #include "HTML.h"        /* The object we will generate */
  70. #include "HTFile.h"
  71. #include "HTAlert.h"
  72.  
  73. /* #include "ParseWSRC.h" */
  74.  
  75. extern int WWW_TraceFlag;    /* Control diagnostic output */
  76. extern FILE * logfile;        /* Log file output */
  77. char *log_file_name = "/dev/null";
  78.  
  79. PRIVATE char    line[2048];    /* For building strings to display */
  80.                 /* Must be able to take id */
  81.  
  82.  
  83. #include "HTParse.h"
  84. #include "HTFormat.h"
  85. #include "HTTCP.h"
  86. /* #include "HTWSRC.h"    */    /* Need some bits from here */
  87.  
  88. /*        Hypertext object building machinery
  89. */
  90. #include "HTML.h"
  91.  
  92. /* #define TRACE 1 */
  93.  
  94. #define PUTC(c) (*target->isa->put_character)(target, c)
  95. #define PUTBLOCK(c, len) (*target->isa->put_block)(target, c, len)
  96. #define PUTS(s) (*target->isa->put_string)(target, s)
  97. #define START(e) (*target->isa->start_element)(target, e, 0, 0)
  98. #define END(e) (*target->isa->end_element)(target, e)
  99. #define END_TARGET (*target->isa->end_document)(target)
  100. #define FREE_TARGET (*target->isa->free)(target)
  101.  
  102. struct _HTStructured {
  103.     CONST HTStructuredClass *    isa;
  104.     /* ... */
  105. };
  106.  
  107. struct _HTStream {
  108.     CONST HTStreamClass *    isa;
  109.     /* ... */
  110. };
  111.  
  112.  
  113. /*                                showDiags
  114. */
  115. /* modified from Jonny G's version in ui/question.c */
  116.  
  117. void showDiags ARGS2(
  118.     HTStream *,         target,
  119.     diagnosticRecord **,     d)
  120. {
  121.   long i;
  122.  
  123.   for (i = 0; d[i] != NULL; i++) {
  124.     if (d[i]->ADDINFO != NULL) {
  125.       PUTS("Diagnostic code is ");
  126.       PUTS(d[i]->DIAG);
  127.       PUTC(' ');
  128.       PUTS(d[i]->ADDINFO);
  129.       PUTC('\n'); ;
  130.     }
  131.   }
  132. }
  133.  
  134. /*    Matrix of allowed characters in filenames
  135. **    -----------------------------------------
  136. */
  137.  
  138. PRIVATE BOOL acceptable[256];
  139. PRIVATE BOOL acceptable_inited = NO;
  140.  
  141. PRIVATE void init_acceptable NOARGS
  142. {
  143.     unsigned int i;
  144.     char * good = 
  145.       "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789./-_$";
  146.     for(i=0; i<256; i++) acceptable[i] = NO;
  147.     for(;*good; good++) acceptable[(unsigned int)*good] = YES;
  148.     acceptable_inited = YES;
  149. }
  150.  
  151. /*    Transform file identifier into WWW address
  152. **    ------------------------------------------
  153. **
  154. **
  155. ** On exit,
  156. **    returns        nil if error
  157. **            pointer to malloced string (must be freed) if ok
  158. */
  159. char * WWW_from_archie ARGS1 (char *, file)
  160. {
  161.     char * end;
  162.     char * result;
  163.     char * colon;
  164.     for(end=file; *end > ' '; end++);    /* assumes ASCII encoding*/
  165.     result = (char *)malloc(10 + (end-file));
  166.     if (!result) return result;        /* Malloc error */
  167.     strcpy(result, "file://");
  168.     strncat(result, file, end-file);
  169.     colon = strchr(result+7, ':');    /* Expect colon after host */
  170.     if (colon) {
  171.     for(; colon[0]; colon[0]=colon[1], colon++);    /* move down */
  172.     }
  173.     return result;
  174. } /* WWW_from_archie */
  175.  
  176. /*    Transform document identifier into URL
  177. **    --------------------------------------
  178. **
  179. ** Bugs: A static buffer of finite size is used!
  180. **    The format of the docid MUST be good!
  181. **
  182. ** On exit,
  183. **    returns        nil if error
  184. **            pointer to malloced string (must be freed) if ok
  185. */
  186. PRIVATE char hex [17] = "0123456789ABCDEF";
  187. extern char from_hex PARAMS((char a));            /* In HTWSRC @@ */
  188.  
  189. PRIVATE char * WWW_from_WAIS ARGS1(any *, docid)
  190. {
  191.   static unsigned char buf[BIG];
  192.   char num[10];
  193.   unsigned char * q = buf;
  194.   char * p = (docid->bytes);
  195.   int i, l;
  196.   if (TRACE) 
  197.     {
  198.       char *p;
  199.       fprintf(stderr, "WAIS id (%d bytes) is ", (int)docid->size);
  200.       for (p = docid->bytes; p < docid->bytes + docid->size; p++) 
  201.         {
  202.           if ((*p >= ' ') && (*p<= '~')) /* Assume ASCII! */
  203.             fprintf(stderr, "%c", *p);
  204.           else
  205.             fprintf(stderr, "<%x>", (unsigned)*p);
  206.         }
  207.       fprintf(stderr, "\n");
  208.     }     
  209.   for (p = docid->bytes; 
  210.        (p < docid->bytes+docid->size) && 
  211.        (q < &buf[BIG]);) 
  212.     {
  213.       if (TRACE) fprintf(stderr, "    Record type %d, length %d\n",
  214.                          p[0], p[1]);
  215.       sprintf(num, "%d", (int)*p);
  216.       bcopy(num, q, strlen(num));
  217.       q += strlen(num);
  218.       p++;
  219.       *q++ = '=';        /* Separate */
  220.       l = (int)((unsigned char)*p);
  221.       p++;
  222.       if (l > 127)
  223.         {
  224.           l = (l - 128) * 128;
  225.           l = l + (int)((unsigned char)*p);
  226.           p++;
  227.         }
  228.       
  229.       for (i = 0; i < l; i++, p++)
  230.         {
  231.           if (!acceptable[(unsigned char)*p]) 
  232.             {
  233.               *q++ = HEX_ESCAPE;
  234.               *q++ = hex[((unsigned char)*p) >> 4];
  235.               *q++ = hex[((unsigned char)*p) & 15];
  236.             }
  237.           else *q++ = (unsigned char)*p;
  238.         }
  239.       *q++= ';';        /* Terminate field */
  240.     }
  241.   *q++ = 0;            /* Terminate string */
  242.   if (TRACE) 
  243.     fprintf(stderr, "WWW form of id: %s\n", buf); 
  244.   {
  245.     char * result = (char *)malloc (strlen (buf) + 1);
  246.     strcpy (result, buf);
  247.     return result;
  248.   }
  249. } /* WWW_from_WAIS */
  250.  
  251.  
  252. /*    Transform URL into WAIS document identifier
  253. **    -------------------------------------------
  254. **
  255. ** On entry,
  256. **    docname        points to valid name produced originally by
  257. **            WWW_from_WAIS
  258. ** On exit,
  259. **    docid->size    is valid
  260. **    docid->bytes    is malloced and must later be freed.
  261. */
  262. PRIVATE any * WAIS_from_WWW ARGS2 (any *, docid, char *, docname)
  263. {
  264.   char *z;     /* Output pointer */
  265.   char *sor;    /* Start of record - points to size field. */
  266.   char *p;     /* Input pointer */
  267.   char *q;     /* Poisition of "=" */
  268.   char *s;     /* Position of semicolon */
  269.   int n;    /* size */
  270.   if (TRACE) 
  271.     fprintf(stderr, "WWW id (to become WAIS id): %s\n", docname); 
  272.   for(n=0, p = docname; *p; p++) 
  273.     {    /* Count sizes of strings */
  274.       n++;
  275.       if (*p == ';')  n--;        /* Not converted */
  276.       else if (*p == HEX_ESCAPE) n=n-2;    /* Save two bytes */
  277.       docid->size = n;
  278.     }
  279.   
  280.   docid->bytes = (char *) malloc(docid->size + 32); /* result record */
  281.   z = docid->bytes;
  282.   
  283.   for(p = docname; *p; ) 
  284.     {
  285.       q = strchr(p, '=');
  286.       if (!q) 
  287.         return 0;
  288.       *q = '\0';
  289.       *z++ = atoi(p);
  290.       *q = '=';
  291.       s = strchr(q, ';');    /* (Check only) */
  292.       if (!s) 
  293.         return 0;    /* Bad! No ';';    */
  294.       sor = z;          /* Remember where the size field was */
  295.       z++;              /* Skip record size for now */
  296.       
  297.       {
  298.         int len;
  299.         int tmp;
  300.     for(p=q+1; *p!=';' ; ) 
  301.           {
  302.             if (*p == HEX_ESCAPE) 
  303.               {
  304.                 char c;
  305.                 unsigned int b;
  306.         p++;
  307.             c = *p++;
  308.         b = from_hex(c);
  309.         c = *p++;
  310.         if (!c) 
  311.                   break;    /* Odd number of chars! */
  312.         *z++ = (b<<4) + from_hex(c);
  313.               } 
  314.             else 
  315.               {
  316.             *z++ = *p++;    /* Record */
  317.               }
  318.           }
  319.         len = (z-sor-1);
  320.         
  321.         z = sor;
  322.         if (len > 127)
  323.           {
  324.             tmp = (len / 128);
  325.             len = len - (tmp * 128);
  326.             tmp = tmp + 128;
  327.             *z++ = (char)tmp;
  328.             *z = (char)len;
  329.           }
  330.         else
  331.           {
  332.             *z = (char)len;
  333.           }
  334.         z++;
  335.       }
  336.       
  337.       for(p=q+1; *p!=';' ; ) 
  338.         {
  339.           if (*p == HEX_ESCAPE) 
  340.             {
  341.               char c;
  342.               unsigned int b;
  343.               p++;
  344.               c = *p++;
  345.               b = from_hex(c);
  346.               c = *p++;
  347.               if (!c) 
  348.                 break;    /* Odd number of chars! */
  349.               *z++ = (b<<4) + from_hex(c);
  350.         } 
  351.           else 
  352.             {
  353.               *z++ = *p++;    /* Record */
  354.         }
  355.     }
  356.       p++;            /* After semicolon: start of next record */
  357.     }
  358.   
  359.   if (TRACE) 
  360.     {
  361.       char *p;
  362.       fprintf(stderr, "WAIS id (%d bytes) is ", (int)docid->size);
  363.       for(p=docid->bytes; p<docid->bytes+docid->size; p++) {
  364.         if ((*p >= ' ') && (*p<= '~'))
  365.           fprintf(stderr, "%c", *p);
  366.         else
  367.           fprintf(stderr, "<%x>", (unsigned)*p);
  368.       }
  369.       fprintf(stderr, "\n");
  370.     }     
  371.   return docid;        /* Ok */
  372.   
  373. } /* WAIS_from_WWW */
  374.  
  375.  
  376. /*    Send a plain text record to the client        output_text_record()
  377. **    --------------------------------------
  378. */
  379.  
  380. PRIVATE void output_text_record ARGS3(
  381.     HTStream *,            target,
  382.     WAISDocumentText *,        record,
  383.     boolean,            quote_string_quotes)
  384. {
  385.   if (record->DocumentText->size)
  386.     {
  387.       /* This cast should be unnecessary, as put_block should operate
  388.          on unsigned char from the start.  What was he thinking??? */
  389.       PUTBLOCK((unsigned char *)record->DocumentText->bytes,
  390.                record->DocumentText->size);
  391.     }    
  392. } /* output text record */
  393.  
  394.  
  395.  
  396. /*    Format A Search response for the client        display_search_response
  397. **    ---------------------------------------
  398. */
  399. /* modified from tracy shen's version in wutil.c
  400.  * displays either a text record or a set of headlines.
  401.  */
  402. void
  403. display_search_response ARGS4(
  404.     HTStructured *,        target,
  405.     SearchResponseAPDU *,    response,
  406.     char *,            database,
  407.     char *,             keywords)
  408. {
  409.   WAISSearchResponse  *info;
  410.   long i, k;
  411.   BOOL archie;
  412.  
  413.   if (!response)
  414.     {
  415.       PUTS("Arrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrgh!");
  416.       return;
  417.     }
  418.  
  419.   archie = strstr(database, "archie")!=0;    /* Specical handling */
  420.   
  421.   if (TRACE) 
  422.     fprintf(stderr, "HTWAIS: Displaying search response\n");
  423.   if (TRACE)
  424.     fprintf (stderr, "HTWAIS: database 0x%08x '%s', response 0x%08x\n",
  425.              database, database, response);
  426.   if (TRACE)
  427.     fprintf (stderr, "HTWAIS: keywords 0x%08x '%s'\n", keywords, keywords);
  428.   sprintf(line,
  429.       "Index %s contains the following %d item%s relevant to '%s'.\n",
  430.      database,
  431.      (int)(response->NumberOfRecordsReturned),
  432.      response->NumberOfRecordsReturned ==1 ? "" : "s",
  433.      keywords);
  434.  
  435.   PUTS(line);
  436.   PUTS("The first figure for each entry is its relative score, ");
  437.   PUTS("the second the number of lines in the item.");
  438.   START(HTML_MENU);
  439.  
  440.   if ( response->DatabaseDiagnosticRecords != 0 ) {
  441.     info = (WAISSearchResponse *)response->DatabaseDiagnosticRecords;
  442.     i =0; 
  443.  
  444.     if (info->Diagnostics != NULL)
  445.       showDiags((HTStream*)target, info->Diagnostics);
  446.  
  447.     if ( info->DocHeaders != 0 ) {
  448.       for (k=0; info->DocHeaders[k] != 0; k++ ) {
  449.     WAISDocumentHeader* head = info->DocHeaders[k];
  450.     char * headline = trim_junk(head->Headline);
  451.     any * docid = head->DocumentID;
  452.     char * docname;            /* printable version of docid */
  453.     i++;
  454.  
  455. /*    Make a printable string out of the document id.
  456. */
  457.     if (TRACE) fprintf(stderr, 
  458.         "HTWAIS:  %2ld: Score: %4ld, lines:%4ld '%s'\n", 
  459.            i,
  460.            (long int)(info->DocHeaders[k]->Score),
  461.            (long int)(info->DocHeaders[k]->Lines),
  462.            headline);
  463.  
  464.     START(HTML_LI);
  465.     sprintf(line, "%4ld  %4ld  ",
  466.         head->Score,
  467.         head->Lines);
  468.     PUTS( line);
  469.  
  470.     if (archie) {
  471.         char * www_name = WWW_from_archie(headline);
  472.         if (www_name) {
  473.                 PUTS ("<A HREF=\"");
  474.                 PUTS (www_name);
  475.                 PUTS ("\">");
  476.         PUTS(headline);
  477.                 PUTS("</A>");
  478.         free(www_name);
  479.         } else {
  480.          PUTS(headline);
  481.          PUTS(" (bad file name)");
  482.         }
  483.     } else { /* Not archie */
  484.         docname =  WWW_from_WAIS(docid);
  485.             if (TRACE)
  486.               fprintf (stderr, "HTWAIS: docname '%s'\n", docname);
  487.         if (docname) {
  488.         char * dbname = HTEscape(database);
  489.                 char types_array[1000]; /* bad */
  490.                 char *type_escaped;
  491.  
  492.                 types_array[0] = 0;
  493.  
  494.                 if (head->Types)
  495.                   {
  496.                     int i;
  497.                     for (i = 0; head->Types[i]; i++)
  498.                       {
  499.                         if (i)
  500.                           strcat (types_array, ",");
  501.  
  502.                         type_escaped = HTEscape (head->Types[i]);
  503.                         strcat (types_array, type_escaped);
  504.                         free (type_escaped);
  505.                       }
  506.                     if (TRACE)
  507.                       fprintf (stderr, "Built types_array '%s'\n", types_array);
  508.                   }
  509.                 else
  510.                   {
  511.                     strcat (types_array, "TEXT");
  512.                   }
  513.                 
  514.         sprintf(line, "%s/%s/%d/%s",
  515.                         dbname,
  516.                         types_array,
  517.                         (int)(head->DocumentLength),
  518.                         docname);
  519.  
  520.                 PUTS ("<A HREF=\"");
  521.                 if (head->Types && head->Types[0] && 
  522.                     strcmp (head->Types[0], "URL") == 0)
  523.                   {
  524.                     /* The real URL, maybe? */
  525.                     if (TRACE)
  526.                       fprintf (stderr, "HTWAIS: Using headline '%s' as URL\n",
  527.                                headline);
  528.                     PUTS(headline);
  529.                   }
  530.                 else
  531.                   {
  532.                     /* Our manufactured URL. */
  533.                     PUTS (line);
  534.                   }
  535.                 PUTS ("\">");
  536.         PUTS(headline);
  537.                 PUTS("</A>");
  538.         free(dbname);
  539.         free(docname);
  540.         } else {
  541.          PUTS("(bad doc id)");
  542.         }
  543.       }
  544.       } /* next document header */
  545.     } /* if there were any document headers */
  546.     
  547.     if ( info->ShortHeaders != 0 ) {
  548.       k =0;
  549.       while (info->ShortHeaders[k] != 0 ) {
  550.     i++;
  551.     PUTS( "(Short Header record, can't display)");
  552.       }
  553.     }
  554.     if ( info->LongHeaders != 0 ) {
  555.       k =0;
  556.       while (info->LongHeaders[k] != 0) {
  557.     i++;
  558.     PUTS( "\nLong Header record, can't display\n");
  559.       }
  560.     }
  561.     if ( info->Text != 0 ) {
  562.       k =0;
  563.       while (info->Text[k] != 0) {
  564.     i++;
  565.     PUTS( "\nText record\n");
  566.     output_text_record((HTStream*)target, info->Text[k++], false);
  567.       }
  568.     }
  569.     if ( info->Headlines != 0 ) {
  570.       k =0;
  571.       while (info->Headlines[k] != 0) {
  572.     i++;
  573.     PUTS( "\nHeadline record, can't display\n");
  574.     /* dsply_headline_record( info->Headlines[k++]); */
  575.       }
  576.     }
  577.     if ( info->Codes != 0 ) {
  578.       k =0;
  579.       while (info->Codes[k] != 0) {
  580.     i++;
  581.     PUTS( "\nCode record, can't display\n");
  582.     /* dsply_code_record( info->Codes[k++]); */
  583.       }
  584.     }
  585.   }                /* Loop: display user info */
  586.   END(HTML_MENU);
  587.   PUTC('\n'); ;
  588. }
  589.  
  590.  
  591. /* ------------------------------------------------------------------------ */
  592. /* ---------------- Local copy of connect_to_server calls ----------------- */
  593. /* ------------------------------------------------------------------------ */
  594.  
  595. /* Returns 1 on success, 0 on fail, -1 on interrupt. */
  596. static int fd_mosaic_connect_to_server(char *host_name, long port, long *fd)
  597. {
  598.   /* New version. */
  599.   char dummy[256];
  600.   int status;
  601.  
  602.   sprintf (dummy, "wais://%s:%d/", host_name, port);
  603.  
  604.   status = HTDoConnect (dummy, "WAIS", 210, (int *)fd);
  605.   if (status == HT_INTERRUPTED)
  606.     {
  607.       if (TRACE)
  608.         fprintf (stderr, "===WAIS=== interrupted in connect\n");
  609.       HTProgress ("Connection interrupted.");
  610.       return -1;
  611.     }
  612.   if (status < 0)
  613.     return 0;
  614.   return 1;
  615. }
  616.  
  617. /* Returns 1 on success, 0 on fail, -1 on interrupt. */
  618. static int mosaic_connect_to_server(char *host_name, long port, FILE **fp)
  619. {
  620.   FILE* file;
  621.   long fd;
  622.   int rv;
  623.   
  624.   rv = fd_mosaic_connect_to_server (host_name, port, &fd);
  625.   if(rv == 0) 
  626.     {
  627.       HTProgress ("Could not connect to WAIS server.");
  628.       return 0;
  629.     }
  630.   else if (rv == -1)
  631.     {
  632.       HTProgress ("Connection interrupted.");
  633.       return -1;
  634.     }
  635.  
  636.   if ((file = fdopen(fd,"r+")) == NULL) 
  637.     {
  638.       HTProgress ("Could not open WAIS connection for reading.");
  639.       return 0;
  640.     }
  641.  
  642.   *fp = file;
  643.   return 1;
  644. }
  645.  
  646.  
  647. /* ------------------------------------------------------------------------ */
  648. /* ------------------------------------------------------------------------ */
  649.     
  650. /* Pulled in from gui.c; corresponds to Rdata.max_wais_responses. */
  651. extern int max_wais_responses;
  652.  
  653. #define MAX_KEYWORDS_LENGTH 5000
  654. #define MAX_SERVER_LENGTH 1000
  655. #define MAX_DATABASE_LENGTH 1000
  656. #define MAX_SERVICE_LENGTH 1000
  657. #define MAXDOCS max_wais_responses
  658.  
  659. /*        Load by name                    HTLoadWAIS
  660. **        ============
  661. **
  662. **    This renders any object or search as required
  663. */
  664. PUBLIC int HTLoadWAIS ARGS4(
  665.     CONST char *,        arg,
  666.     HTParentAnchor *,    anAnchor,
  667.     HTFormat,        format_out,
  668.     HTStream*,        sink)
  669. {
  670.   char *key;              /* pointer to keywords in URL */
  671.   char *request_message = NULL; /* arbitrary message limit */
  672.   char *response_message = NULL; /* arbitrary message limit */
  673.   long request_buffer_length;    /* how of the request is left */
  674.   SearchResponseAPDU  *retrieval_response = 0;
  675.   char keywords[MAX_KEYWORDS_LENGTH + 1];
  676.   char *server_name;    
  677.   char *wais_database = NULL;        /* name of current database */
  678.   char *www_database;            /* Same name escaped */
  679.   char *service;
  680.   char *doctype;
  681.   char *doclength;
  682.   long document_length;
  683.   char *docname;
  684.   FILE *connection = 0;
  685.   char *names;        /* Copy of arg to be hacked up */
  686.   BOOL ok = NO;
  687.   WAISSearchResponse *response;
  688.   diagnosticRecord **diag;
  689.   
  690.   if (!acceptable_inited) 
  691.     init_acceptable();
  692.   
  693.   /*    Decipher and check syntax of WWW address:
  694.    **    ----------------------------------------
  695.    **
  696.    **    First we remove the "wais:" if it was spcified.  920110
  697.    */  
  698.   names = HTParse(arg, "", PARSE_HOST | PARSE_PATH | PARSE_PUNCTUATION);
  699.   key = strchr(names, '?');
  700.   
  701.   if (key) 
  702.     {
  703.       char * p;
  704.       *key++ = 0;    /* Split off keywords */
  705.       for (p=key; *p; p++) if (*p == '+') *p = ' ';
  706.       HTUnEscape(key);
  707.     }
  708.   if (names[0]== '/') 
  709.     {
  710.       server_name = names+1;
  711.       if (*server_name == '/')
  712.         server_name++;    /* Accept one or two */
  713.       www_database = strchr(server_name,'/');
  714.       if (www_database) 
  715.         {
  716.           *www_database++ = 0;        /* Separate database name */
  717.           doctype = strchr(www_database, '/');
  718.           if (key) 
  719.             ok = YES;    /* Don't need doc details */
  720.           else if (doctype) 
  721.             {    /* If not search parse doc details */
  722.               *doctype++ = 0;    /* Separate rest of doc address */
  723.               doclength = strchr(doctype, '/');
  724.               if(doclength) 
  725.                 {
  726.                   *doclength++ = 0;
  727.                   
  728.                   /* OK, now doclength should be the rest of the URL,
  729.                      right??? */
  730.                   if (TRACE)
  731.                     fprintf (stderr, 
  732.                              "WAIS: doctype '%s', doclength\n~~~~\n%s\n~~~~\n",
  733.                              doctype, doclength);
  734.                   /* Multitype! */
  735.                   if (strchr (doctype, ','))
  736.                     {
  737.                       HTStructured *target = 
  738.                         HTML_new(anAnchor, format_out, sink);
  739.                       char *t, *oldt, *newt, *revised;
  740.                       int first;
  741.  
  742.                       if (TRACE)
  743.                         {
  744.                           fprintf (stderr,
  745.                                    "WAIS: Hey boss, got multitype.\n");
  746.                           fprintf (stderr,
  747.                                    "WAIS: names is '%s'\n", names);
  748.                         }
  749.  
  750.                       START(HTML_TITLE);
  751.                       PUTS("Multiple Format Opportunity");
  752.                       END(HTML_TITLE);
  753.                       
  754.                       START(HTML_H1);
  755.                       PUTS("Multiple Format Opportunity");
  756.                       END(HTML_H1);
  757.  
  758.                       PUTS("This is a multiformat WAIS response.  You may pick the format of your choice from the list that follows: <p>\n\n<ul>\n");
  759.  
  760.                       /* Get the first doctype. */
  761.                       t = strtok (doctype, ",");
  762.  
  763.                       /* oldt is a copy of the first doctype,
  764.                          with leading period. */
  765.                       oldt = (char *)malloc (strlen (t) + 16);
  766.                       sprintf (oldt, ".%s", t);
  767.                       
  768.                       first = 1;
  769.  
  770.                       while (t && *t)
  771.                         {
  772.                           /* Got a type, as t. */
  773.                           PUTS("<li> <a href=\"wais:");
  774.                           PUTS(names);
  775.                           PUTS("/");
  776.                           PUTS(www_database);
  777.                           PUTS("/");
  778.                           PUTS(t);
  779.                           PUTS("/");
  780.                           PUTS(doclength);
  781.                           PUTS("\">");
  782.                           
  783.                           /* Unescape t in place; we don't need it anymore
  784.                              after this anyway. */
  785.                           HTUnEscape (t);
  786.                           PUTS(t);
  787.                           PUTS("</a>\n");
  788.                           t = strtok (NULL, ",");
  789.                         }
  790.  
  791.                       free (oldt);
  792.                       
  793.                       PUTS("</ul>");
  794.                       
  795.                       END_TARGET;
  796.                       FREE_TARGET;
  797.                       
  798.                       /* Hey, WE'RE DONE! */
  799.                       free (names);
  800.                       return HT_LOADED;
  801.                     }
  802.                   
  803.                   document_length = atol(doclength);
  804.                   if (document_length) 
  805.                     {
  806.                       docname=strchr(doclength, '/');
  807.                       if (docname) 
  808.                         {
  809.                           *docname++ = 0;
  810.                           ok = YES;    /* To avoid a goto! */
  811.                         } /* if docname */
  812.                     } /* if document_length valid */
  813.                 } /* if doclength */
  814.             } 
  815.           else 
  816.             { /* no doctype?  Assume index required */
  817.               if (!key) key = "";
  818.               ok = YES;
  819.             } /* if doctype */
  820.         } /* if database */
  821.     }
  822.   
  823.   if (!ok)
  824.     {
  825.       free (names);
  826.       return HT_NOT_LOADED;
  827.     }
  828.  
  829.   if (TRACE) 
  830.     fprintf(stderr, "HTWAIS: Parsed OK; type is '%s'\n", doctype);
  831.  
  832.   service = strchr(names, ':');
  833.   if (service)  
  834.     *service++ = 0;
  835.   else 
  836.     service = "210";
  837.   
  838.   if (server_name[0] == 0)
  839.     connection = NULL;
  840.   else if (!(key && !*key))
  841.     {
  842.       int status;
  843.       if (TRACE)
  844.         fprintf (stderr, "===WAIS=== calling mosaic_connect_to_server\n");
  845.       status = mosaic_connect_to_server
  846.         (server_name, atoi(service), &connection);
  847.       if (status == 0)
  848.         {
  849.           if (TRACE)
  850.             fprintf (stderr, "===WAIS=== connection failed\n");
  851.           free(names);
  852.           return HT_NOT_LOADED;
  853.         }
  854.       else if (status == -1)
  855.         {
  856.           if (TRACE)
  857.             fprintf (stderr, "===WAIS=== connection interrupted\n");
  858.           free(names);
  859.           return HT_INTERRUPTED;
  860.         }
  861.     }
  862.  
  863.   StrAllocCopy(wais_database,www_database);
  864.   HTUnEscape(wais_database);
  865.   
  866.   /* This below fixed size stuff is terrible */
  867.   request_message = (char*)s_malloc((size_t)MAX_MESSAGE_LEN * sizeof(char));
  868.   response_message = (char*)s_malloc((size_t)MAX_MESSAGE_LEN * sizeof(char));
  869.   
  870.   /*    If keyword search is performed but there are no keywords,
  871.    **    the user has followed a link to the index itself. It would be
  872.    **    appropriate at this point to send him the .SRC file - how?
  873.    */
  874.   if (key && !*key) 
  875.     {                /* I N D E X */
  876.       HTStructured * target = HTML_new(anAnchor, format_out, sink);
  877.       
  878.       START(HTML_TITLE);
  879.       PUTS(wais_database);
  880.       PUTS(" index");
  881.       END(HTML_TITLE);
  882.       
  883.       START(HTML_H1);
  884.       PUTS(wais_database);
  885.       END(HTML_H1);
  886.  
  887.       START(HTML_ISINDEX);
  888.       
  889.       START(HTML_P);
  890.       
  891.       END_TARGET;
  892.       if (connection) 
  893.         close_connection(connection);
  894.       FREE_TARGET;
  895.     } 
  896.   else if (key) 
  897.     {                    /* S E A R C H */
  898.       char *p;
  899.       HTStructured * target;
  900.       
  901.       strncpy(keywords, key, MAX_KEYWORDS_LENGTH);
  902.       while(p=strchr(keywords, '+')) 
  903.         *p = ' ';
  904.       
  905.       /* Send advance title to get something fast to the other end */
  906.       
  907.       target = HTML_new(anAnchor, format_out, sink);
  908.       
  909.       START(HTML_TITLE);
  910.       PUTS(keywords);
  911.       PUTS(" (in ");
  912.       PUTS(wais_database);
  913.       PUTS(")");
  914.       END(HTML_TITLE);
  915.       
  916.       START(HTML_H1);
  917.       PUTS(keywords);
  918.       END(HTML_H1);
  919.       
  920.       START(HTML_ISINDEX);
  921.       
  922.       request_buffer_length = MAX_MESSAGE_LEN; /* Amount left */
  923.       if (TRACE) fprintf(stderr, "HTWAIS: Search for `%s' in `%s'\n",
  924.                          keywords, wais_database);
  925.       
  926.       if(NULL == generate_search_apdu(request_message + HEADER_LENGTH, 
  927.                                       &request_buffer_length, 
  928.                                       keywords, wais_database, NULL, MAXDOCS))
  929.         {
  930.           HTProgress ("WAIS request too large; something went wrong.");
  931.         }
  932.       
  933.       if(!interpret_message(request_message, 
  934.                             MAX_MESSAGE_LEN - request_buffer_length, 
  935.                             response_message,
  936.                             MAX_MESSAGE_LEN,
  937.                             connection,
  938.                             false    /* true verbose */
  939.                             )) 
  940.         {
  941.           HTProgress ("WAIS returned message too large; something went wrong.");
  942.         } 
  943.       else 
  944.         {    /* returned message ok */
  945.           SearchResponseAPDU  *query_response = 0;
  946.           readSearchResponseAPDU(&query_response,
  947.                                  response_message + HEADER_LENGTH);
  948.           /* We do want this to be called if !query_response, to
  949.              get our cute error message. */
  950.           display_search_response(target, 
  951.                                   query_response, wais_database, keywords);
  952.           if (query_response)
  953.             {
  954.               if (query_response->DatabaseDiagnosticRecords)
  955.                 freeWAISSearchResponse(query_response->DatabaseDiagnosticRecords);
  956.               freeSearchResponseAPDU( query_response);
  957.             }
  958.         }    /* returned message not too large */
  959.       
  960.       END_TARGET;
  961.       if (connection) 
  962.         close_connection(connection);
  963.       FREE_TARGET;
  964.     } 
  965.   else 
  966.     {            /* D O C U M E N T    F E T C H */
  967.       HTFormat format_in;
  968.       HTStream * target;
  969.       long count;
  970.       any   doc_chunk;
  971.       any * docid = &doc_chunk;
  972.  
  973.       if (doctype)
  974.         HTUnEscape (doctype);
  975.  
  976.       if (TRACE)
  977.         fprintf(stderr,
  978.                 "===WAIS=== Retrieve document id `%s' type `%s' length %ld\n",
  979.                 docname, doctype, document_length);
  980.       
  981.       {
  982.         char *tmp_doctype = strdup (doctype);
  983.         char *tmp;
  984.         for (tmp = tmp_doctype; *tmp; tmp++)
  985.           *tmp = TOLOWER (*tmp);
  986.         if (TRACE)
  987.           fprintf (stderr,
  988.                    "===WAIS=== Doing HTAtom_exists on '%s'\n", tmp_doctype);
  989.         format_in = HTAtom_exists (tmp_doctype);
  990.         free (tmp_doctype);
  991.       }
  992.       if (!format_in)
  993.         {
  994.           char dummy[256];
  995.           HTAtom *pencoding;
  996.           int compressed;
  997.           
  998.           sprintf (dummy, "foo.%s", doctype);
  999.           format_in = HTFileFormat (dummy, &pencoding,
  1000.                                     WWW_PLAINTEXT, &compressed);
  1001.           /* Assume it will always be at *least* WWW_PLAINTEXT. */
  1002.         }
  1003.       
  1004.       target = HTStreamStack(format_in, format_out, 0, sink, anAnchor);
  1005.       if (!target) 
  1006.         {
  1007.           free (names);
  1008.           if (connection) 
  1009.             close_connection(connection);
  1010.           return HT_NOT_LOADED;
  1011.         }
  1012.  
  1013.       /*    Decode hex or literal format for document ID
  1014.        */    
  1015.       WAIS_from_WWW(docid, docname);
  1016.       
  1017.       /*    Loop over slices of the document
  1018.        */    
  1019.       {
  1020.         int bytes = 0, intr;
  1021.         char line[256];
  1022.         
  1023.         HTClearActiveIcon ();
  1024.         
  1025.         count = 0;
  1026.         while (1)
  1027.           {
  1028.             char *type = s_strdup(doctype);    /* Gets freed I guess */
  1029.             request_buffer_length = MAX_MESSAGE_LEN; /* Amount left */
  1030.             if (TRACE) 
  1031.               fprintf(stderr, "HTWAIS: Slice number %ld\n", count);
  1032.             
  1033.             intr = HTCheckActiveIcon (1);
  1034.             if (intr)
  1035.               {
  1036.                 HTProgress ("Data transfer interrupted.");
  1037.                 (*target->isa->handle_interrupt)(target);
  1038.                 free (names);
  1039.                 if (connection) 
  1040.                   close_connection(connection);
  1041.                 return HT_INTERRUPTED;
  1042.               }
  1043.             
  1044.             if(generate_retrieval_apdu
  1045.                (request_message + HEADER_LENGTH,
  1046.                 &request_buffer_length, 
  1047.                 docid, 
  1048.                 CT_byte,
  1049.                 count * CHARS_PER_PAGE,
  1050.                 (count + 1) * CHARS_PER_PAGE,
  1051.                 type,
  1052.                 wais_database
  1053.                 ) == 0)
  1054.               {
  1055.                 HTProgress 
  1056.                   ("WAIS error condition; retrieval may be unsuccessful.");
  1057.               }
  1058.  
  1059.             free (type);
  1060.             
  1061.             /*    Actually do the transaction given by request_message */   
  1062.             if(0 ==
  1063.                interpret_message
  1064.                (request_message, 
  1065.                 MAX_MESSAGE_LEN - request_buffer_length, 
  1066.                 response_message,
  1067.                 MAX_MESSAGE_LEN,
  1068.                 connection,
  1069.                 false /* true verbose */    
  1070.                 ))
  1071.               {
  1072.                 HTProgress ("WAIS error condition; retrieval may be unsuccessful.");
  1073.                 goto no_more_data;
  1074.               }
  1075.             
  1076.             /*     Parse the result which came back into memory.
  1077.              */
  1078.             readSearchResponseAPDU(&retrieval_response, 
  1079.                                    response_message + HEADER_LENGTH);
  1080.             
  1081.             response = 
  1082.               (WAISSearchResponse *)retrieval_response->DatabaseDiagnosticRecords;
  1083.             diag = response->Diagnostics;
  1084.  
  1085.             if(NULL == response->Text)
  1086.               {
  1087.                 if (TRACE)
  1088.                   fprintf (stderr, "WAIS: no more data (NULL response->Text)\n");
  1089.                 if (retrieval_response->DatabaseDiagnosticRecords)
  1090.                   freeWAISSearchResponse 
  1091.                     (retrieval_response->DatabaseDiagnosticRecords);
  1092.                 freeSearchResponseAPDU (retrieval_response);
  1093.                 goto no_more_data;
  1094.               } 
  1095.             else if
  1096.               (((WAISSearchResponse *)
  1097.                 retrieval_response->DatabaseDiagnosticRecords)->Text[0]->DocumentText->size)
  1098.               {
  1099.                 output_text_record
  1100.                   (target,
  1101.                    ((WAISSearchResponse *)
  1102.                     retrieval_response->DatabaseDiagnosticRecords)->Text[0],
  1103.                    false);
  1104.               } /* If text existed */
  1105.             else
  1106.               {
  1107.                 if (TRACE)
  1108.                   fprintf (stderr, "WAIS: no more data (fell through)\n");
  1109.                 if (retrieval_response->DatabaseDiagnosticRecords)
  1110.                   freeWAISSearchResponse 
  1111.                     (retrieval_response->DatabaseDiagnosticRecords);
  1112.                 freeSearchResponseAPDU (retrieval_response);
  1113.                 goto no_more_data;
  1114.               }
  1115.  
  1116.             /* Slightly inaccurate for last slice. */
  1117.             bytes += CHARS_PER_PAGE;
  1118.             sprintf (line, "Read %d bytes of data.", bytes);
  1119.             HTProgress (line);
  1120.  
  1121.             if (diag &&
  1122.                 diag[0] &&
  1123.                 diag[0]->ADDINFO != NULL &&
  1124.                 !strcmp(diag[0]->DIAG, D_PresentRequestOutOfRange))
  1125.               {
  1126.                 if (TRACE)
  1127.                   fprintf (stderr, "WAIS: no more data (diag)\n");
  1128.                 if (retrieval_response->DatabaseDiagnosticRecords)
  1129.                   freeWAISSearchResponse 
  1130.                     (retrieval_response->DatabaseDiagnosticRecords);
  1131.                 freeSearchResponseAPDU (retrieval_response);
  1132.                 goto no_more_data;
  1133.               }
  1134.  
  1135.             if (retrieval_response->DatabaseDiagnosticRecords)
  1136.               freeWAISSearchResponse 
  1137.                 (retrieval_response->DatabaseDiagnosticRecords);
  1138.             freeSearchResponseAPDU (retrieval_response);
  1139.  
  1140.             count++;
  1141.           }    /* Loop over slices */
  1142.         
  1143.       } /* local variables */
  1144.  
  1145.     no_more_data:
  1146.       
  1147.       END_TARGET;
  1148.       /* Close the connection BEFORE calling system(), which can
  1149.          happen in the free method. */
  1150.       if (connection) 
  1151.         close_connection(connection);
  1152.       FREE_TARGET;
  1153.       
  1154.       free (docid->bytes);
  1155.     } /* If document rather than search */
  1156.   
  1157.   if (wais_database) 
  1158.     free (wais_database);
  1159.   s_free (request_message);
  1160.   s_free(response_message);
  1161.   
  1162.   free(names);
  1163.   return HT_LOADED;
  1164. }
  1165.  
  1166. PUBLIC HTProtocol HTWAIS = { "wais", HTLoadWAIS, NULL };
  1167.  
  1168. #endif /* DIRECT_WAIS */
  1169.